package com.threegold.listview;

import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.List;

public abstract class ListItemProvider<T, VH extends ListView.ViewHolder> extends BaseItemProvider {
    /**
     * 第一个Header对应的itemType类型常量
     */
    private static final int ITEM_TYPE_HEADER = 7000;
    /**
     * 第一个Footer对应的itemType类型常量
     */
    private static final int ITEM_TYPE_FOOTER = 8000;
    /**
     * 加载更多LoadMoreView对应的itemType类型常量
     */
    private static final int ITEM_TYPE_LOAD_MORE = 9000;
    /**
     * emptyView对应的itemType类型常量
     */
    private static final int ITEM_TYPE_EMPTY = 6001;

    /**
     * 适配器对应的ListView对象
     */
    private ListView listView;

    /**
     * 数据集合对象
     */
    private List<T> dataList;
    /**
     * 上下文对象
     */
    protected Context context;
    /**
     * 设置数据
     */
    public void setDataList(List<T> dataList) {
        this.dataList = dataList;
        // 如果没有HeaderView，直接根据数据判断是否需要展示emptyView
        if (listView != null) {
            listView.displayEmptyView(getHeaderCount() == 0 && getFooterCount() == 0 && getDataSize() == 0);
            int size = dataList == null ? 0 : dataList.size();
            // 如果这里直接使用notifyDataChanged方法刷新数据，会偶现数据没有更新，
            // 使用notifyDataSetItemRangeInserted方法更新具体范围就可以解决该问题
            if (size > 0) {
                notifyDataSetItemRangeInserted(0, size);
            } else {
                notifyDataChanged();
            }
        }
    }

    /**
     * 获取数据
     */
    public List<T> getDataList() {
        return dataList;
    }

    public int getDataSize() {
        return dataList == null ? 0 : dataList.size();
    }

    @Override
    public T getItem(int position) {
        int total = getDataSize();
        if (position > 0 && position <= total - 1) {
            return dataList.get(position);
        }
        return null;
    }

    /**
     * 传入ListView，会使用到ListView获取一些配置参数
     */
    void bindListView(ListView listView) {
        this.listView = listView;
        this.context = listView.getContext();
    }

    /**
     * 是否可以加载更多：必须打开加载更多开关，并且LoadMoreView已经设置好了，才可以正常使用加载更多功能
     */
    private boolean canLoadMore() {
        return listView.isOpenLoadMore() && getLoadMoreLayoutView() != null;
    }

    /**
     * 是否展示emptyView
     */
    private boolean showEmptyView() {
        return listView.isDisplayEmpty() && getEmptyView() != null;
    }

    private Component getEmptyView() {
        return listView.getEmptyView();
    }

    protected int getColumnCount() {
        return listView.getColumnNo();
    }

    private int getHeaderCount() {
        return listView.getHeaderViewCount();
    }

    private int getFooterCount() {
        return listView.getFooterViewCount();
    }

    @Override
    public int getCount() {
        // 展示emptyView的时候只有一个item
        if (showEmptyView()) {
            return 1;
        }
        return getItemCount() + getHeaderCount() + getFooterCount() + (canLoadMore() ? 1 : 0);
    }

    /**
     * 这里返回实际内容的item个数，需要根据设置的列数进行计算
     */
    private int getItemCount() {
        // 默认认为内容item个数为0，下面根据dataList重新计算个数
        int contentCount = 0;
        if (dataList != null) {
            int column = getColumnCount();
            int dataSize = dataList.size();
            contentCount = dataSize % column == 0 ? dataSize / column : dataSize / column + 1;
        }
        return contentCount;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemComponentType(int position) {
        if (showEmptyView()) {
            return ITEM_TYPE_EMPTY;
        }
        if (isLoadMoreItem(position)) {
            return ITEM_TYPE_LOAD_MORE;
        }
        if (isHeaderItem(position)) {
            return ITEM_TYPE_HEADER + position;
        }
        if (isFooterItem(position)) {
            return ITEM_TYPE_FOOTER + getPositionInFooterIndex(position);
        }
        // 多列展示的内容目前只能够支持一种itemType
        // 原因：如果一行出现多种itemType的itemView，复用时候判断就非常复杂，目前不支持这种情况
        // 因此如果设置为多列，在onCreateViewHolder的时候不能使用itemViewType作为布局id去创建item
        if (getColumnCount() > 1) {
            return 0;
        }
        return getItemType(position);
    }

    private int getPositionInFooterIndex(int position) {
        // 获取position在footerView集合中的索引值
        int footerStartPosition = getHeaderCount() + getItemCount();
        return position - footerStartPosition;
    }

    private boolean isFooterItem(int position) {
        // 如果是footer的索引，那么必须满足条件：
        // 1、必须有footer，即footerCount > 0；
        // 2、索引值必须在footer对应的索引值范围，即 position >= HeaderCount+itemCount，且< HeaderCount+itemCount+FooterCount；
        int footerCount = getFooterCount();
        int footerStartPosition = getHeaderCount() + getItemCount();
        int footerEndPosition = footerStartPosition + footerCount;
        return footerCount > 0 && position >= footerStartPosition && position < footerEndPosition;
    }

    private boolean isHeaderItem(int position) {
        // Header数量大于0，并且当前索引在Header数量内，那么表示当前索引位置的组件就是HeaderView中对应索引的
        int headerCount = getHeaderCount();
        return headerCount > 0 && position < headerCount;
    }

    private boolean isLoadMoreItem(int position) {
        return canLoadMore() && getCount() == position + 1;
    }

    /**
     *  注意事项：系统机制是会复用所有已经出屏幕被缓存的item对应的View，由于HeaderView、LoadMoreView、EmptyView都是唯一的，
     *  并且不能够用在dataList数据对应的item复用里面（因为没有绑定对应的ViewHolder），因此处理方法有两种：
     *  1、在执行getContentComponent方法前判断是否是复用了上面3中itemView，如果是，则直接将复用的component设置为null。
     *  2、在getContentComponent中单列和多列复用component时，从component中获取tag，在强转为ViewHolder的时判断tag不为空，且绑定了ViewHolder。
     *
     *  推荐做法：为了保证绝对的安全不出问题，因此这里使用第二种方法。
     */
    @Override
    public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
        if (showEmptyView()) {
            return getEmptyView();
        }
        // 支持加载更多，并且是最后一个item，那么直接返回LoadMoreView
        if (isLoadMoreItem(position)) {
            return getLoadMoreLayoutView();
        }
        if (isHeaderItem(position)) {
            return listView.getHeaderView(position);
        }
        if (isFooterItem(position)) {
            return listView.getFooterView(getPositionInFooterIndex(position));
        }
        // 剩下的就是这里就是具体的内容item
        return getContentComponent(position - getHeaderCount(), component, componentContainer);
    }

    /**
     * 处理除开HeaderView和LoadMoreView的item的组件获取逻辑（包含单列和多列展示）
     */
    private Component getContentComponent(int position, Component component, ComponentContainer parent) {
        int column = getColumnCount();
        int itemViewType = getItemType(position);
        // 单列的时候代表item的索引
        if (column == ListView.DEFAULT_COLUMN) {
            return getDefaultColumnComponent(position, itemViewType, component, parent);
        }
        // 多列时候，position就代表第几行：getCount()方法已经通过总列数计算，因此这里返回的就是行号
        return getMultipleColumnComponent(position, itemViewType, column, component);
    }

    /**
     * 创建一个新的多列ViewHolder对象
     */
    private ColumnsViewHolder<VH> createNewColumnsViewHolder(int columnNo, int itemViewType) {
        ColumnsViewHolder<VH> columnsViewHolder = new ColumnsViewHolder<>(listView);
        for (int i = 0; i < columnNo; i++) {
            // 注意：添加的时候每一行都添加满，否则会导致当前行的itemView宽度不一致
            // 将每一列的item添加到线性布局中
            VH itemHolder = onCreateViewHolder(columnsViewHolder.getRow(), itemViewType);
            columnsViewHolder.addViewHolder(itemHolder, i == 0, i == columnNo - 1);
        }
        return columnsViewHolder;
    }

    /**
     * 处理多列item组件的复用逻辑，并返回当前索引的itemView对象
     * @param rowNo 当前行索引
     * @param itemViewType 当前行对应的各个item的type类型
     * @param columnNo 当前行总列数
     * @param component 当前行复用的组件对象
     */
    private Component getMultipleColumnComponent(int rowNo, int itemViewType, int columnNo, Component component) {
        ColumnsViewHolder<VH> columnsViewHolder;
        // 可以复用的条件：component不为空，且tag为ColumnsViewHolder的实例（多列已经限制，这里不必考虑tag对应的itemViewType与传入的itemViewType相同）
        if (component != null && (component.getTag() instanceof ListItemProvider.ColumnsViewHolder)) {
            columnsViewHolder = (ColumnsViewHolder<VH>) component.getTag();
        } else {
            columnsViewHolder = createNewColumnsViewHolder(columnNo, itemViewType);
            component = columnsViewHolder.getRow();
        }

        List<VH> columnItemViewHolders = columnsViewHolder.getColumnItemViewHolders();
        Row rowLayout = columnsViewHolder.getRow();
        for (int i = 0; i < columnItemViewHolders.size(); i++) {
            VH itemViewHolder = columnItemViewHolders.get(i);
            int columnItemPosition = rowNo * columnNo + i;
            int totalDataSize = dataList.size();
            Component itemChild = rowLayout.getItemChild(i);
            // 当复用多列的一行线性布局的时候，最后一行的item数量可能会比column小，
            // 因此这里要判断，展示和绑定数据只能够处理数据范围内的数据，超出范围的需要隐藏
            boolean overRange = columnItemPosition >= totalDataSize;
            if (!overRange) {
                // 注意：多列每一个item实际的宽度 = (row宽度 - columnDivider * 数量 ) / columnNo
                // 获取每一行的宽度值
                int rowWidth = listView.getWidth() - listView.getPaddingLeft() - listView.getPaddingRight();
                int rowDividerCount = rowLayout.isShowColumnOuterDivider() ? getColumnCount() + 1 : getColumnCount() - 1;
                int itemWidth = (int) (1.0f * (rowWidth - rowDividerCount * rowLayout.getColumnDividerWidth()) / getColumnCount());
                // 绑定数据
                T data = dataList.get(columnItemPosition);
                onBindViewHolder(itemViewHolder, columnItemPosition, data, itemWidth);
                // 设置item点击事件
                setItemViewClickListener(itemChild, data, columnItemPosition);
                // 设置item长按事件
                setItemViewLongClickListener(itemChild, data, columnItemPosition);
            }
            // 这里最好不要使用HIDE，如果组件被HIDE，再次调用VISIBLE的时候存在bug，可能不会显示，这种情况偶现的
            itemChild.setVisibility(overRange ? Component.INVISIBLE : Component.VISIBLE);
        }
        // 计算是否是最后一行：这里跟上面计算overRange不一样，加法用的i索引从0开始，这里加法使用的直接列数量，从1开始的
        // 因此判断是有等于号，代表没有超过，最后一行填满就是等号情况
        boolean isLastRow = dataList.size() - rowNo * columnNo <= columnNo;
        // 当前行显示的itemView列数：不是最后一行默认展示所有列
        int displayColumn = columnNo;
        if (isLastRow) {
            displayColumn = dataList.size() - rowNo * columnNo;
        }
        // 刷新row的效果
        rowLayout.refreshEffect(isLastRow, rowNo == 0, columnNo, displayColumn);
        return component;
    }

    /**
     * 处理单列item组件的复用逻辑，并返回当前索引的itemView对象
     */
    private Component getDefaultColumnComponent(int position, int itemViewType, Component component, ComponentContainer parent) {
        VH viewHolder;
        // 复用条件：component不为空，绑定的tag(即ViewHolder)对应的itemViewType相等
        if (component != null && (component.getTag() instanceof ListView.ViewHolder)
                && ((ListView.ViewHolder)component.getTag()).getItemViewType() == itemViewType) {
            viewHolder = (VH) component.getTag();
        } else {
            viewHolder = onCreateViewHolder(parent, itemViewType);
            viewHolder.getItemView().setTag(viewHolder);
        }
        T data = dataList.get(position);
        // 绑定数据
        onBindViewHolder(viewHolder, position, data, viewHolder.getItemView().getWidth());
        // 设置item点击事件
        setItemViewClickListener(viewHolder.getItemView(), data, position);
        // 设置item长按事件
        setItemViewLongClickListener(viewHolder.getItemView(), data, position);
        return viewHolder.getItemView();
    }

    /**
     * 设置itemView的点击事件
     * @param itemView 对应的item的视图组件
     * @param position 当前item的索引
     */
    private void setItemViewClickListener(Component itemView, T data, int position) {
        itemView.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                if (listView.getOnItemClickListener() != null) {
                    listView.getOnItemClickListener().onItemClick(component, data, position);
                }
            }
        });
    }

    /**
     * 设置itemView的长按事件
     * @param itemView 对应的item的视图组件
     * @param position 当前item的索引
     */
    private void setItemViewLongClickListener(Component itemView, T data, int position) {
        itemView.setLongClickedListener(new Component.LongClickedListener() {
            @Override
            public void onLongClicked(Component component) {
                if (listView.getOnItemLongClickListener() != null) {
                    listView.getOnItemLongClickListener().onItemLongClick(component, data, position);
                }
            }
        });
    }

    private Component getLoadMoreLayoutView() {
        return listView.getLoadMoreLayoutView();
    }

    /**
     * 获取当前itemView对应的type类型
     * @param position 索引
     */
    protected int getItemType(int position) {
        return 0;
    }

    /**
     * 创建ViewHolder的钩子：使用者处理
     *
     * 使用注意：如果ListView的columnNo > 1，getItemComponentType方法强制返回type=0，
     * 如果重写getItemType方法，使用布局id作为返回值，这种情况是不会返回对应的布局id值，而是固定为0
     *
     * @param parent itemView对应的父组件容器
     * @param itemViewType 当前itemView对应的itemType
     * @return 当前itemView对应的ViewHolder对象
     */
    protected abstract VH onCreateViewHolder(ComponentContainer parent, int itemViewType);

    /**
     * 列表每次展示到屏幕时候绑定数据钩子：使用者处理
     * @param viewHolder 当前itemView对应的ViewHolder对象
     * @param position 当前itemView对应的数据索引
     * @param data 当前itemView对应的数据
     */
    protected abstract void onBindViewHolder(VH viewHolder, int position, T data, int itemWidth);

    /**
     * 用于多列排布时候的一行ViewHolder对象
     */
    private static class ColumnsViewHolder<VH extends ListView.ViewHolder> {
        /**
         * 用于缓存当前行中每一列对应的ViewHolder对象
         */
        private final List<VH> columnItemViewHolders = new ArrayList<>();
        /**
         * 当前行组件对象
         */
        private final Row row;

        public ColumnsViewHolder(ListView listView) {
            // 构建row对象
            row = new Row.Builder(listView.getContext())
                    .setShowColumnOuterDivider(listView.isShowColumnOuterDivider())
                    .setColumnDividerColor(listView.getColumnDividerColor())
                    .setColumnDividerSize(listView.getColumnDividerSize())
                    .setRowDividerColor(listView.getRowDividerColor())
                    .setRowDividerSize(listView.getRowDividerSize())
                    .setRowDividerIgnoreDisplayColumn(listView.isRowDividerIgnoreDisplayColumn())
                    .setShowBottomDivider(listView.isShowBottomRowDivider())
                    .setShowTopRowDivider(listView.isShowTopRowDivider())
                    .build();
            // 设置总权重
            row.setTotalWeight(listView.getColumnNo());
            // 设置布局配置
            row.setLayoutConfig(
                    new ComponentContainer.LayoutConfig(
                            ComponentContainer.LayoutConfig.MATCH_PARENT,
                            ComponentContainer.LayoutConfig.MATCH_CONTENT
                    )
            );
            // 将当前ViewHolder设置到row的tag中保存
            row.setTag(this);
        }

        /**
         * 往当前行中添加item
         * @param viewHolder 持有该item的ViewHolder
         * @param lastColumn 是否是最后一列
         */
        public void addViewHolder(VH viewHolder, boolean isFirstColumn, boolean lastColumn) {
            // 添加到ViewHolder的缓存集合中
            columnItemViewHolders.add(viewHolder);
            // 添加ViewHolder对应的item到当前行中
            row.addRowItemChild(viewHolder.getItemView(), isFirstColumn, lastColumn);
        }

        public List<VH> getColumnItemViewHolders() {
            return columnItemViewHolders;
        }

        public Row getRow() {
            return row;
        }
    }

    @Override
    public void notifyDataSetItemChanged(int position) {
        super.notifyDataSetItemChanged(position + getHeaderCount());
    }

    @Override
    public void notifyDataSetItemInserted(int position) {
        super.notifyDataSetItemInserted(position + getHeaderCount());
    }

    @Override
    public void notifyDataSetItemRangeRemoved(int startPos, int countItems) {
        super.notifyDataSetItemRangeRemoved(startPos + getHeaderCount(), countItems);
    }

    @Override
    public void notifyDataSetItemRemoved(int position) {
        super.notifyDataSetItemRemoved(position + getHeaderCount());
    }

    @Override
    public void notifyDataSetItemRangeInserted(int startPos, int countItems) {
        super.notifyDataSetItemRangeInserted(startPos + getHeaderCount(), countItems);
    }

    @Override
    public void notifyDataSetItemRangeChanged(int startPos, int countItems) {
        super.notifyDataSetItemRangeChanged(startPos + getHeaderCount(), countItems);
    }
}
